home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 May / macformat-024.iso / Shareware City / Developers / nshellmegasource1.50 / mega src / commands / tail.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-19  |  13.7 KB  |  596 lines  |  [TEXT/KAHL]

  1. /* ========== the commmand file: ==========
  2.  
  3.     tail.c
  4.     
  5.     Copyright (c) 1994 Newport Software Development
  6.     
  7.     You may distribute unmodified copies of this file for
  8.     noncommercial purposes.  You may use this file as a
  9.     reference when writing your own nShell(tm) commands.
  10.     
  11.     All other rights are reserved.
  12.     
  13.    ========== the commmand file: ========== */
  14.  
  15. #define    TAIL_BUF_SIZE    2048
  16.  
  17. #ifdef __MWERKS__            // CodeWarrior requires an A4 setup
  18. #include <A4Stuff.h>
  19. #endif
  20.  
  21. #include "nshc.h"
  22.  
  23. #include "arg_utl.proto.h"
  24. #include "fss_utl.proto.h"
  25. #include "nshc_utl.proto.h"
  26. #include "str_utl.proto.h"
  27.  
  28. /* ======================================== */
  29.  
  30. // typedefs
  31.  
  32. // The main data structure, the one that becomes nshc_parms->data
  33.  
  34. typedef enum { file_none, file_open, file_read, file_write, file_close } f_state;
  35.  
  36. typedef struct {
  37.  
  38.     short    arg;            // position in arg list
  39.     short    n_arg;            // non-zero if an -n option was used
  40.     short    by_file;        // 1 if files are listed, 0 if stdin
  41.     
  42.     long    lines;            // the line count for the current file
  43.     long    lines_max;        // the line limit
  44.     
  45.     short    fref;            // file ref. number, 0 if no file open (when by file)
  46.     long    fpos;            // position in the current file (when by file)
  47.     f_state    fstate;            // a state machine control used when searching files
  48.     
  49.     Handle    root;            // start of linked list of strings (used with console)
  50.     Handle    last;            // end of linked list (used with console)
  51.  
  52.     short    got_fss;        // 0 if FSSpec calls are not available
  53.  
  54. } t_tail_data;
  55.  
  56. typedef    t_tail_data    **TailHndl;
  57.  
  58. // This structure holds strings typed on standard input until they are echoed
  59.  
  60. typedef struct {
  61.  
  62.         Handle    next;
  63.         char    string[];
  64.         
  65. } t_string_rec;
  66.  
  67. typedef t_string_rec **t_string_hndl;
  68.  
  69. // This structure holds string positions from files until they are echoed
  70.  
  71. typedef struct {
  72.  
  73.         Handle    next;
  74.         long    position;
  75.         
  76. } t_position_rec;
  77.  
  78. typedef t_position_rec **t_position_hndl;
  79.  
  80. /* ======================================== */
  81.  
  82. // prototypes - utility
  83.  
  84. void tail_bad( t_nshc_parms *nshc_parms, int code );
  85. void tail_bad_file( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, StringPtr msg );
  86. void tail_good( t_nshc_parms *nshc_parms );
  87.  
  88. // prototypes - file copy routines
  89.  
  90. void  tail_open( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_tail_data **hData );
  91. void  tail_read( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_tail_data **hData );
  92. void  tail_write( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_tail_data **hData );
  93. void  tail_close( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_tail_data **hData );
  94. void  tail_file( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_tail_data **hData );
  95.  
  96. // prototypes - console copy routines
  97.  
  98. void  tail_copy( char *q, char *p );
  99. OSErr tail_string_add( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_tail_data **hData, char *buf );
  100. void  tail_string_print( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_tail_data **hData );
  101. void  tail_console( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_tail_data **hData );
  102.  
  103. // prototypes - state machine
  104.  
  105. void tail_start( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls  );
  106. void tail_continue( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls );
  107. void tail_stop( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls );
  108.  
  109. /* ======================================== */
  110.  
  111. // utility routines
  112.  
  113. /* ======================================== */
  114.  
  115. void tail_bad(  t_nshc_parms *nshc_parms, int code )
  116. {
  117.     nshc_parms->action = nsh_stop;
  118.     nshc_parms->result = code;
  119. }
  120.  
  121. void tail_bad_file(  t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, StringPtr msg )
  122. {
  123.     nshc_calls->NSH_putStr_err("\ptail: File access error (");
  124.     nshc_calls->NSH_putStr_err(msg);
  125.     nshc_calls->NSH_putStr_err("\p)\r");
  126.  
  127.     nshc_parms->action = nsh_stop;
  128.     nshc_parms->result = NSHC_ERR_FILE;
  129. }
  130.  
  131. void tail_good(  t_nshc_parms *nshc_parms )
  132. {
  133.     nshc_parms->action = nsh_stop;
  134.     nshc_parms->result = 0;
  135. }
  136.  
  137. /* ======================================== */
  138.  
  139. // file access routines
  140.  
  141. /* ======================================== */
  142.  
  143. void tail_open( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_tail_data **hData )
  144. {
  145.     int        result;
  146.     long    dirID;
  147.     Boolean    isDir;
  148.     FSSpec    fss;
  149.     
  150.     // by definition, there is no open file, and it has no lines
  151.     
  152.     (**hData).fref = 0;
  153.     (**hData).lines = 0;
  154.     (**hData).fpos = 0;
  155.  
  156.     // skip the -n option
  157.     
  158.     if ( (**hData).arg == (**hData).n_arg )
  159.         (**hData).arg += 2;
  160.         
  161.     if ( (**hData).arg >= nshc_parms->argc ) {
  162.         tail_good( nshc_parms );
  163.         return;
  164.         }
  165.     
  166.     // =====> convert path to fsspec
  167.     
  168.     result = arg_to_fss( nshc_parms, nshc_calls, (**hData).arg, &fss );
  169.  
  170.     (**hData).arg++;
  171.     
  172.     if (result) {
  173.         tail_bad( nshc_parms, result );
  174.         return;
  175.         }
  176.     
  177.     result = fss_to_DirID( &fss, &dirID, &isDir );
  178.         
  179.     if (( result == noErr) && isDir)
  180.         return;
  181.             
  182.     if ( result == fnfErr ) {
  183.         nshc_calls->NSH_putStr_err("\ptail: File not found = ");
  184.         nshc_calls->NSH_putStr_err((StringPtr)fss.name);
  185.         nshc_calls->NSH_putchar('\r');
  186.         return;
  187.         }
  188.             
  189.     if (!result)
  190.         result = fss_OpenDF((**hData).got_fss, &fss, fsRdPerm, &(**hData).fref);
  191.     
  192.     if (!result) {
  193.         HLock( hData );
  194.         result = GetEOF((**hData).fref, &(**hData).fpos );
  195.         HUnlock( hData );
  196.         }
  197.         
  198.     if ( result )
  199.         tail_bad_file( nshc_parms, nshc_calls, (StringPtr)fss.name );
  200.         
  201.     (**hData).fstate = file_read;
  202. }
  203.  
  204. /* ======================================== */
  205.  
  206. void tail_read( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_tail_data **hData )
  207. {
  208.     int        i;
  209.     int        result;
  210.     long    bcount;
  211.     char    c;
  212.     char    buf[TAIL_BUF_SIZE+1];
  213.     
  214.     result = 0;
  215.     
  216.     // =====> determine and set file position
  217.     
  218.     bcount = TAIL_BUF_SIZE;
  219.     (**hData).fpos -= TAIL_BUF_SIZE;
  220.     
  221.     if ( (**hData).fpos < 0 ) {        // adjust buf size if we hit the
  222.         bcount += (**hData).fpos;    // beginning of the file
  223.         (**hData).fpos = 0;
  224.         }
  225.     
  226.     result = SetFPos( (**hData).fref, fsFromStart, (**hData).fpos );
  227.     
  228.     if (result) {
  229.         tail_bad_file( nshc_parms, nshc_calls, "\pset position" );
  230.         return;
  231.         }
  232.  
  233.     // =====> read the data fork
  234.     
  235.     result = FSRead( (**hData).fref, &bcount, &buf );
  236.     
  237.     if (( result == noErr ) || ( result == eofErr )) {
  238.     
  239.         if (bcount) {
  240.         
  241.             buf[bcount] = '\0';                    // make sure there is termination
  242.             
  243.             i = bcount - 1;
  244.             
  245.             while ( i >= 0 ) {                    // search backwards for n lines
  246.                 c = buf[i];
  247.                 if ( c == '\r' )
  248.                     (**hData).lines++;
  249.                 if ( (**hData).lines > (**hData).lines_max ) {    // print if found
  250.                     nshc_calls->NSH_puts( &buf[i+1] );
  251.                     (**hData).fstate = file_write;
  252.                     break;
  253.                     }
  254.                 if ( !(**hData).fpos && !i) {                    // print if beginning
  255.                     nshc_calls->NSH_puts( buf );                // of file is hit
  256.                     (**hData).fstate = file_write;                // before n lines
  257.                     break;                                        // are found
  258.                     }
  259.                 i--;
  260.                 }
  261.  
  262.             }
  263.             
  264.         if (result == eofErr) {
  265.             (**hData).fstate = file_write;
  266.             result = 0;
  267.             }
  268.                 
  269.         }
  270.     else
  271.         tail_bad_file( nshc_parms, nshc_calls, "\pread data" );
  272.     
  273. }
  274.  
  275.  
  276. /* ======================================== */
  277.  
  278. void tail_write( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_tail_data **hData )
  279. {
  280.     int        result;
  281.     long    bcount;
  282.     char    buf[TAIL_BUF_SIZE+1];
  283.     
  284.     result = 0;
  285.     
  286.     bcount = TAIL_BUF_SIZE;
  287.     result = FSRead( (**hData).fref, &bcount, &buf );
  288.     
  289.     if (( result == noErr ) || ( result == eofErr )) {
  290.     
  291.         if (result == eofErr) {
  292.             (**hData).fstate = file_close;
  293.             result = 0;
  294.             }
  295.         
  296.         if (bcount) {
  297.             buf[bcount] = '\0';
  298.             nshc_calls->NSH_puts( buf );
  299.             }
  300.         
  301.         }
  302.     else
  303.         tail_bad_file( nshc_parms, nshc_calls, "\pwrite data" );
  304.     
  305. }
  306.  
  307. /* ======================================== */
  308.  
  309. void tail_close( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_tail_data **hData )
  310. {
  311.     OSErr    result;
  312.     
  313.     result = 0;
  314.     
  315.     if ( (**hData).fref ) {
  316.         result = FSClose( (**hData).fref );
  317.         (**hData).fref = 0;
  318.         }
  319.         
  320.     if (result)
  321.         tail_bad_file( nshc_parms, nshc_calls, "\pclose file" );    
  322.         
  323.     (**hData).fstate = file_open;        // start the next file, if any    
  324. }
  325.  
  326. /* ======================================== */
  327.  
  328. void tail_file( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_tail_data **hData )
  329. {
  330.     switch ((**hData).fstate) {
  331.         case file_open:
  332.             tail_open( nshc_parms, nshc_calls, hData );
  333.             break;
  334.         case file_read:
  335.             tail_read( nshc_parms, nshc_calls, hData );
  336.             break;
  337.         case file_write:
  338.             tail_write( nshc_parms, nshc_calls, hData );
  339.             break;
  340.         case file_close:
  341.             tail_close( nshc_parms, nshc_calls, hData );
  342.             break;
  343.         }
  344. }
  345.  
  346. /* ======================================== */
  347.  
  348. // console access routines
  349.  
  350. /* ======================================== */
  351.  
  352. // list management routines
  353.  
  354. void tail_copy( char *q, char *p )
  355. {
  356.     char c;
  357.     
  358.     while ( c = *p++ )
  359.         *q++ = c;
  360.         
  361.     *q = 0;
  362. }
  363.  
  364. OSErr tail_string_add( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_tail_data **hData, char *buf )
  365. {
  366.     short    length;
  367.     Handle    new_element;
  368.     Handle    old_element;
  369.     
  370.     length = 8 + cStrLen( buf );    // room for handle plus some padding
  371.     
  372.     new_element = NewHandleClear( length );
  373.     
  374.     if ( !new_element )
  375.         return( NSHC_ERR_MEMORY );
  376.         
  377.     HLock( new_element );
  378.     tail_copy( (**(t_string_hndl)new_element).string, buf );
  379.     HUnlock( new_element );
  380.     
  381.     (**(t_string_hndl)(**hData).last).next = new_element;
  382.     (**hData).last = new_element;
  383.  
  384.     if ( !(**hData).root )
  385.         (**hData).root = new_element;
  386.         
  387.     if ( (**hData).lines > (**hData).lines_max ) {
  388.         old_element = (**(t_string_hndl)(**hData).root).next;
  389.         DisposeHandle( (**hData).root );
  390.         (**hData).root = old_element;
  391.         }
  392.  
  393.     (**hData).lines++;
  394.     
  395.     return( NSHC_NO_ERR );
  396. }
  397.  
  398. /* ======================================== */
  399.  
  400. void tail_string_print( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_tail_data **hData )
  401. {
  402.     Handle    this;
  403.     Handle    next;
  404.     
  405.     this = (**hData).root;        // detach list from data block
  406.     (**hData).root = nil;
  407.         
  408.     while ( this ) {            // print and delete each element
  409.         HLock( this );
  410.         nshc_calls->NSH_puts( (**(t_string_hndl)this).string );
  411.         HUnlock( this );
  412.         next = (**(t_string_hndl)this).next;
  413.         DisposeHandle( this );
  414.         this = next;
  415.         }
  416. }
  417.  
  418. /* ======================================== */
  419.  
  420. #define    LINES_PER_PASS    10
  421.  
  422. void tail_console( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls, t_tail_data **hData )
  423. {
  424.     long    bcount;
  425.     char    buf[LINE_MAX];
  426.     char    c;
  427.     char    *p;
  428.     int        pass;
  429.     
  430.     // if characters are waiting, pick up a few lines
  431.         
  432.     pass = 1;
  433.     
  434.     while ( pass++ < LINES_PER_PASS ) {
  435.     
  436.         bcount = nshc_calls->NSH_gets( buf, LINE_MAX );
  437.         
  438.         if (!bcount) return;                // go get more characters
  439.         
  440.         if ( tail_string_add( nshc_parms, nshc_calls, hData, buf ) ) {
  441.             nshc_calls->NSH_putStr_err( "\ptail: Could not allocate enough string storage.\r" );
  442.             tail_bad( nshc_parms, NSHC_ERR_MEMORY );
  443.             return;
  444.             }
  445.             
  446.         if ( bcount == -1 ) {                // end of input, set-up for exit
  447.             tail_string_print( nshc_parms, nshc_calls, hData );
  448.             tail_good( nshc_parms );
  449.             return;
  450.             }
  451.             
  452.         }
  453. }
  454.  
  455. /* ======================================== */
  456.  
  457. // state machine - core routines
  458.  
  459. /* ======================================== */
  460.  
  461. void tail_start( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls  )
  462. {
  463.     TailHndl    hData;    // handle to hold our data
  464.     int            usage;  // if 1, usage error
  465.     
  466.     nshc_parms->action = nsh_continue;
  467.  
  468.     hData = (TailHndl)NewHandleClear(sizeof(t_tail_data));
  469.     
  470.     if (hData) {
  471.     
  472.         (**hData).arg = 1;                            // start at the arg = 1 position
  473.         (**hData).got_fss = fss_test();                // test if we can use FSSpec calls
  474.         (**hData).lines_max = 10;                    // do ten lines as default
  475.         (**hData).fstate = file_open;                // if we do files, start with open
  476.             
  477.         usage = 0;
  478.         
  479.         if ( (**hData).n_arg = nshc_got_option( nshc_parms, 'n' ) ) {
  480.         
  481.             usage = 1;
  482.         
  483.             if ( nshc_is_numeric_operand( nshc_parms, (**hData).n_arg + 1 ) ) {
  484.                 (**hData).lines_max = arg_to_num( nshc_parms, (**hData).n_arg + 1 );
  485.                 (**hData).by_file = nshc_parms->argc > 3;    // if we have other args, do file i/o
  486.                 usage = 0;
  487.                 }
  488.                 
  489.             }
  490.         else
  491.             (**hData).by_file = nshc_parms->argc > 1;    // if we have other args, do file i/o
  492.         
  493.         if ( usage ) {
  494.             nshc_calls->NSH_putStr_err( "\pUsage: tail [-n lines] [files...]\r" );
  495.             tail_bad( nshc_parms, NSHC_ERR_MEMORY );
  496.             }
  497.         
  498.         if ( (**hData).lines_max < 1 ) {
  499.             nshc_calls->NSH_putStr_err( "\ptail: Line count must be greater than one.\r" );
  500.             tail_bad( nshc_parms, NSHC_ERR_MEMORY );
  501.             }
  502.         
  503.         nshc_parms->data = (Handle)hData;
  504.         }
  505.     else {
  506.         nshc_calls->NSH_putStr_err( "\ptail: Could not allocate storage.\r" );
  507.         tail_bad( nshc_parms, NSHC_ERR_MEMORY );
  508.         }
  509. }
  510.  
  511. /* ======================================== */
  512.  
  513. void tail_continue( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls )
  514. {
  515.     TailHndl    hData;
  516.  
  517.     if (hData = (TailHndl)nshc_parms->data) {
  518.     
  519.         if ( (**hData).by_file )
  520.             tail_file( nshc_parms, nshc_calls, hData );
  521.         else
  522.             tail_console( nshc_parms, nshc_calls, hData );
  523.         
  524.         }
  525. }
  526.  
  527. /* ======================================== */
  528.  
  529. void tail_stop( t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls )
  530. {
  531.     short        fRef;
  532.     TailHndl    hData;
  533.     int            temp;
  534.     int            result;
  535.     Handle        this;
  536.     Handle        next;
  537.     
  538.     result = 0;
  539.     
  540.     if (hData = (TailHndl)nshc_parms->data) {
  541.     
  542.         this = (**hData).root;                        // detach list from data block
  543.         (**hData).root = nil;
  544.         
  545.         while ( this ) {                            // delete each element
  546.             next = (**(t_string_hndl)this).next;
  547.             DisposeHandle( this );
  548.             this = next;
  549.             }
  550.     
  551.         if ( (**hData).fref ) {
  552.             if ( temp = FSClose( (**hData).fref ) )
  553.                 result = temp;
  554.             (**hData).fref = 0;
  555.             }
  556.             
  557.         DisposeHandle(nshc_parms->data);
  558.         }
  559.         
  560.     if (result)
  561.         tail_bad_file( nshc_parms, nshc_calls, "\pclosing files" );
  562.         
  563.     nshc_parms->action = nsh_idle;
  564. }
  565.  
  566. /* ======================================== */
  567.  
  568. void main(t_nshc_parms *nshc_parms, t_nshc_calls *nshc_calls)
  569. {
  570. #ifdef __MWERKS__
  571.     long oldA4  = SetCurrentA4();
  572. #endif
  573.     
  574.     if ( !nshc_bad_version( nshc_parms, nshc_calls, NSHC_VERSION ) ) {
  575.     
  576.         switch (nshc_parms->action) {
  577.             case nsh_start:
  578.                 tail_start(nshc_parms, nshc_calls);
  579.                 break;
  580.             case nsh_continue:
  581.                 tail_continue(nshc_parms, nshc_calls);
  582.                 break;
  583.             case nsh_stop:
  584.                 tail_stop(nshc_parms, nshc_calls);
  585.                 break;
  586.             }
  587.         
  588.         }
  589.  
  590. #ifdef __MWERKS__
  591.     SetA4(oldA4);
  592. #endif
  593. }
  594.  
  595. /* ======================================== */
  596.